package com.ruoyi.HKISAPI.demo.function.basic.guard;

import com.ruoyi.HKISAPI.demo.entity.DeviceInfoDTO;
import com.ruoyi.HKISAPI.demo.entity.DeviceLinkStatusDTO;
import com.ruoyi.HKISAPI.demo.entity.enums.ContentTypeEnum;
import com.ruoyi.HKISAPI.demo.function.basic.AlarmGuardDemo;
import com.ruoyi.HKISAPI.utils.AlarmDataParser;
import com.ruoyi.HKISAPI.utils.ConfigFileUtil;
import com.ruoyi.HKISAPI.utils.HTTPClientUtil;
import com.ruoyi.HKISAPI.utils.HttpsClientUtil;
import com.ruoyi.electronicFence.mapper.TEfAlarmsMapper;
import com.ruoyi.electronicFence.service.ITEfAlarmsService;
import com.ruoyi.rsikManage.mapper.MonitorPointMapper;
import com.ruoyi.rsikManage.service.MonitorPointService;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.client.methods.AsyncCharConsumer;
import org.apache.http.protocol.HttpContext;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author zhengxiaohui
 * @date 2024/1/10 11:39
 * @desc
 */

public class ResponseConsumer extends AsyncCharConsumer<Boolean> {
    private CloseableHttpAsyncClient closeableHttpAsyncClient;

    private DeviceInfoDTO deviceInfo;

    private final CopyOnWriteArrayList<Character> chBuffer;

//    private AlarmDataParser alarmDataParser = new AlarmDataParser();
    // 假设这里是在一个非 Spring 管理的地方，如单元测试或者手动创建对象的情况下

    @Autowired
    private  ITEfAlarmsService iTEfAlarmsService;
    @Autowired
    private  MonitorPointService monitorPointService;
    AlarmDataParser alarmDataParser = new AlarmDataParser();

    public ResponseConsumer(CloseableHttpAsyncClient closeableHttpAsyncClient,
                            DeviceInfoDTO deviceInfo,
                            CopyOnWriteArrayList<Character> chBuffer) {
        this.closeableHttpAsyncClient = closeableHttpAsyncClient;
        this.deviceInfo = deviceInfo;
        this.chBuffer = chBuffer;
    }

    // 消息类型
    private int curHttpContentType;

    /**
     * 实际的boundary类型
     */
    private String curHttpBoundary;

    @Override
    protected void onResponseReceived(final HttpResponse response) {
        // 确定消息类型
        String tbuf = response.toString();
        System.out.println("onResponseReceived: " + tbuf);
        curHttpContentType = ContentTypeEnum.getEventType(tbuf);
        // 获取实际的boundary信息
        for (Header headerItem : response.getAllHeaders()) {
            if (headerItem.getName().contains("Content-Type")) {
                String headerValue = headerItem.getValue();
                for (String item : headerValue.split(";")) {
                    if (item.contains(boundary)) {
                        curHttpBoundary = item.split("=")[1];
                    }
                }
            }
        }
    }

    // 接收消息的回调函数
    @Override
    protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
        System.out.println("数据接收");
        DeviceLinkStatusDTO devLongLinkStatus = AlarmGuardDemo.devLinkStatusMap.get(deviceInfo);
        devLongLinkStatus.dataRecv.set(true);
        AlarmGuardDemo.devLinkStatusMap.put(deviceInfo, devLongLinkStatus);

        StringBuilder strBuilder = new StringBuilder();
        // 按照消息类型解析
        switch (curHttpContentType) {
            case ContentTypeEnum.MULTIPART_FORM_DATA: {
                for (int i = 0; i < buf.length(); i++) {
                    // 填充缓冲区
                    chBuffer.add(buf.charAt(i));
                    strBuilder.append(buf.charAt(i));
                }
                if (strBuilder.toString().contains("--" + curHttpBoundary)) {
                    // 一次表单数据解析
                    parseMultiData();
                }
                break;
            }
            default: {
                System.out.println("未匹配到可以解析的content-type, 请自行处理");
            }
        }

        devLongLinkStatus = AlarmGuardDemo.devLinkStatusMap.get(deviceInfo);
        if (devLongLinkStatus.stopLink.get()) {
            // 停止布防
            buf.clear();
            chBuffer.clear();
            this.close();
            devLongLinkStatus.stopLink.set(true);
            AlarmGuardDemo.devLinkStatusMap.put(deviceInfo, devLongLinkStatus);
        }
    }

    @Override
    protected Boolean buildResult(final HttpContext context) {
        return Boolean.TRUE;
    }

    private static final int HeadSize = 256;
    private static final String end = "\r\n";
    private static final String boundary = "boundary=";
    private static final String ContentT = "Content-Type: ";
    private static final String ContentL = "Content-Length: ";
    private static final String ContentID = "Content-ID: ";

    /**
     * 转换multipart类型的表单数据逻辑
     */
    private void parseMultiData() {
        // [一] 取固定的长度size转换为字符串判断当前boundary中的内容信息
        int offset = 0; // 标定实际报文内容的起始位置
        int infoType = 0; // 单个boundary header头标定的数据类型
        if (chBuffer.isEmpty() || chBuffer.size() < HeadSize) {
            return;
        }
        List<Character> targetList = chBuffer.subList(0, HeadSize);
        StringBuilder targetBuf = new StringBuilder();
        for (char tempNode : targetList) {
            targetBuf.append(tempNode);
        }
        String strHeadBuf = targetBuf.toString();
        // [二] 判断单个boundary的内容类型
        if (strHeadBuf.contains(ContentT)) {
            offset += strHeadBuf.indexOf(ContentT);
            infoType = ContentTypeEnum.getEventType(strHeadBuf);
        }

        // [三] 获取当前要处理的boundary的Content-Length信息用于后续从缓冲区中取数据
        StringBuilder strlen = new StringBuilder();
        int contentLength = 0;
        if (strHeadBuf.contains(ContentL)) {
            // 如果strHeadBuf中有ContentID字段，对该字段进行解析
            if (strHeadBuf.contains(ContentID)) {
                offset = strHeadBuf.indexOf(ContentL);
                offset += ContentL.length();
                // 获得表单单元的长度
                for (int i = 0; strHeadBuf.charAt(offset) != '\n'; offset++) {
                    strlen.append(strHeadBuf.charAt(offset));
                    i++;
                }
                String str = strlen.toString().trim();
                contentLength = Integer.parseInt(str);

                // 将数据偏移量定位到ContentID字段之后
                offset = strHeadBuf.indexOf(ContentID);
                for (int i = 0; strHeadBuf.charAt(offset) != '\r'; offset++) {
                    i++;
                }
            } else {
                offset = strHeadBuf.indexOf(ContentL);
                offset += ContentL.length();
                // 获得表单单元的长度
                for (int i = 0; strHeadBuf.charAt(offset) != '\r'; offset++) {
                    strlen.append(strHeadBuf.charAt(offset));
                    i++;
                }
                String str = strlen.toString().trim();
                contentLength = Integer.parseInt(str);
            }
        }

        // 定位到offset的所在位置
        offset = strHeadBuf.indexOf(end + "" + end);
        if (-1 == offset) {
            System.out.println("could not found http header and body separate symbol : \\r\\n\\r\\n");
            return;
        }
        // \r\n\r\n 到正式的报文体开始为止
        offset += (2 * end.length());
        // 先判断下当前接收到的缓存内容是否在当前boundary的length之内
        //  - 已经接收到boundary单元： 根据类型做代码切割处理
        //  - 未接收完boundary单元： 继续接收
        if (chBuffer.size() < offset + contentLength) {
            return;
        }
        // [四] 从缓冲区中截取Content-Length长度的数据内容
        String strContent = null;
        byte[] picByte = null;
        switch (infoType) {
            case ContentTypeEnum.APPLICATION_JSON:
            case ContentTypeEnum.APPLICATION_XML: {
                // 从缓冲区中取出boundary的length数据
                StringBuilder strBodyBuf = new StringBuilder();
                targetList = chBuffer.subList(offset, offset + contentLength);
                for (char c : targetList) {
                    strBodyBuf.append(c);
                }
                strContent = strBodyBuf.toString();
                // 从缓冲区中移除掉已经取出的数据信息
                removeFromBuffer(offset, contentLength);
                break;
            }
            case ContentTypeEnum.IMAGE_JPEG:
            case ContentTypeEnum.IMAGE_PNG:
            case ContentTypeEnum.VIDEO_MPG:
            case ContentTypeEnum.VIDEO_MPEG4:
            case ContentTypeEnum.APPLICATION_ZIP: {
                if (chBuffer.size() > offset + contentLength) {
                    picByte = new byte[contentLength];
                    targetList = chBuffer.subList(offset, offset + contentLength);
                    for (int i = 0; i < contentLength; i++) {
                        picByte[i] = (byte) targetList.get(i).charValue();
                    }
                    // 从缓冲区中移除掉已经取出的数据信息
                    removeFromBuffer(offset, contentLength);
                }
                break;
            }
            // TODO 如果有其他的类型在这里添加类型处理逻辑
            default: {
                System.out.println("未匹配到可以解析的content-type, 请自行补全处理!");
            }
        }
        // [五] 截取出的数据进行响应的格式转换（json/xml/图片数据等）
        alarmDataParser.parseAlarmInfo(infoType, "/Users/wangxiaoxing/Desktop/sql/"+".jpg", strContent, picByte);
    }

    /**
     * 从接收缓冲区中移除指定的数据
     *
     * @param offset        移除开始位置
     * @param contentLength 移除内容长度
     */
    private void removeFromBuffer(int offset, int contentLength) {
        for (int i = 0; i < (offset + contentLength + end.length()) && chBuffer.size() > 0; i++) {
            chBuffer.remove(0);
        }
    }
}
